﻿// Learn more about F# at http://fsharp.net

module exprMain =

(* ### QUESTIONS 
    1) Expr string should be generated at runtime or when the tree state is setted to invalid and eval1 has been called?
    2) measureString problem
    3) myTextBox: OnSelectionChanged: use of boolean var: ignore.
    4) Type Expr 
    5) Parser
    6) Centrare espressione

*)



    open System.Windows.Forms
    open System.Drawing
    open System.Drawing.Drawing2D
    open System.Text.RegularExpressions



    type mouseEvent = 
    | Click
    | Over

    //Contiene informazioni relative ad un evento associato al mouse (mouseEvent)
    type mouseInfo(handle, event, position:PointF) =
        let mutable _handle = handle
        let mutable _event = event
        let mutable _pos = position
    
        //UNUSED. Specifica se l'evento deve essere ancora gestito.
        member x.handle 
            with get() = _handle
            and set h = _handle <- h
    
        //Il mouseEvent associato.
        member x.event 
            with get() = _event
            and set e = _event <- e
        
        //La posizione del cursore
        member x.position 
            with get() = _pos
            and set p = _pos <- p
    
    //Eccezione lanciata in caso la stringa di input non sia ritenuta valida.
    type InvalidExpr() = 
        inherit System.Exception() 
    

    //Definisce l'espressione. Rappresentata come un albero.
    type Expr0 =
    | BigSum of Expr*Expr*Expr
    | Ratio of Expr*Expr
    | Sum of Expr*Expr
    | Sub of Expr*Expr
    | Exponent of Expr*Expr
    | Subscript of Expr*Expr
    | Literal of string
    | Round of Expr
    | Equals of Expr*Expr
    | Empty
    and Expr(Expr0) = //Contenitore di un Expr0. Fornisce informazioni relative all'espressione associata.
    
        let mutable _rect  = new RectangleF() //vedi x.Box
        let mutable _container = new RectangleF() //vedi x.Container
        let mutable _expr = Expr0 //vedi x.Expr
        let mutable _exprstring = "" //vedi x.ExprString
        let mutable _offset = new PointF() //vedi x.Offset
        let mutable _middleY = 0.f //vedi x.MiddleY
        let mutable _over = false //vedi x.MouseOver
        let mutable _clicked = false //vedi x.Clicked
        let mutable _graphicPath = new System.Drawing.Drawing2D.GraphicsPath()

        //Espressione associata.
        member x.Expr 
            with get() = _expr
            and set e = _expr <- e

        //Rettangolo che definisce l'area di disegno del simbolo o della stringa (literal) dell'espressione.
        //Tali misure sono relative alla posizione del padre.
        member x.Box 
            with get() = _rect
            and set b1 = _rect <- b1
    
        //Rettangolo che definisce l'area di disegno dell'intera espressione x.Expr.
        ////Tali misure sono relative alla posizione del padre.
        member x.Container 
            with get() = _container
            and set c1 = 
                _container <- c1
    
        //Offset che
        member x.Offset 
            with get() = _offset
            and set o = _offset <- o
    
    
        member x.MiddleY 
            with get() = _middleY
            and set m = _middleY <- m
    
    
        member x.ExprString
            with get() = _exprstring
            and set e = _exprstring <- e 
    
    
        member x.MouseOver 
            with get() = _over
            and set s = _over <- s
    
    
        member x.Clicked 
            with get() = _clicked
            and set c = _clicked <- c

        member x.DrawRectagle = RectangleF(x.Offset.X + x.Box.X, x.Offset.Y + x.Box.Y, x.Box.Width, x.Box.Height)
    
        member x.DrawContainer = RectangleF(x.Offset.X, x.Offset.Y, x.Container.Width, x.Container.Height)
           
        member x.GraphicPath
            with get() = _graphicPath
            and set g = _graphicPath <- g

    //Sum(Literal("2"), Literal("2"))


    //let mutable selectedNode = Expr(Empty)

    let stdfont = "Times New Roman"

    let fontsize (level:int, big) = if (big) then 64.f - float32 (2 * level) else 22.f - float32 (4 * level) 
    
    let font(level, big) = new Font(stdfont, fontsize(level, big))

    let measureString (g:Graphics, str, font) : RectangleF =
        let sz1 = g.MeasureString(str, font)
    
        //Define bounding rectangle of _toRender
        let mutable myRectangle1 = new RectangleF(new PointF(0.f, 0.f), sz1)
    
        let chranges = [| new CharacterRange(0, str.Length) |]
    
        let sf = new StringFormat()
        sf.FormatFlags <- StringFormatFlags.NoClip
        sf.SetMeasurableCharacterRanges(chranges)
    
        let mutable sz = new SizeF(0.f, 0.f)
    
        let mutable miny = 0.f
    
    
        if str<>"" then    
            let regions = g.MeasureCharacterRanges(str, font, myRectangle1, sf)
    
            sz <- new SizeF((*regions.[0].GetBounds(arg).Width*) sz1.Width, regions.[0].GetBounds(g).Height)
        sf.Dispose()
    
       // debug("measureString: " + str + "; " + sz.ToString())
        //Il rettangolo che contiene this
        new RectangleF(new PointF(0.f, 0.f), sz)
    
    
    
    

    let centerX (baseRect:RectangleF) (toCenter:RectangleF) = baseRect.X + baseRect.Width / 2.f - toCenter.Width / 2.f

    let centerRelX (baseRect:RectangleF) (toCenter:RectangleF) = baseRect.Width / 2.f - toCenter.Width / 2.f

    let centerY (baseRect:RectangleF) (toCenter:RectangleF) = baseRect.Y + baseRect.Height / 2.f - toCenter.Height / 2.f

    let centerRelY (baseRect:RectangleF) (toCenter:RectangleF) = baseRect.Height / 2.f - toCenter.Height / 2.f

//    let alignBynaryOp(expr1:Expr byref, expr2:Expr byref, symbolBox:RectangleF byref) : unit =
//        
//                let localmiddleY = System.Math.Max(expr1.MiddleY, expr2.MiddleY)
//                let mutable expr1Box = expr1.Container
//                let mutable expr2Box = expr2.Container
//                expr1Box.Y <- localmiddleY - expr1.MiddleY
//                expr2Box.Y <- localmiddleY - expr2.MiddleY
//                symbolBox.Y <- localmiddleY - symbolBox.Height / 2.f
//
//                ()
   
    let rec eval1 (g:Graphics, node:Expr, level:int) =
        let mutable container = node.Container
        match node.Expr with
        |BigSum(p1, p2, arg) ->

            //let symbol = "Σ"
            //let mutable sybox = measureString(g, symbol, font(level, true))
            let mutable sybox = new RectangleF(0.f, 0.f, 56.f, 56.f)
            container <- sybox
            eval1 (g, p1, level+1)
            eval1(g, p2, level+1)
            eval1(g, arg, level)
            node.ExprString <- "bigsum(" + p1.ExprString + ", " + p2.ExprString + "," + arg.ExprString + ")"

            let mutable p1box = p1.Container
            let mutable p2box = p2.Container
            let mutable argbox = arg.Container
        
            p2box.Location <- new PointF(centerRelX sybox p2box, 0.f)
            container.Height <- container.Height + p2box.Height
        
            sybox.Y <- p2box.Height
        

            p1box.Location <- new PointF(centerRelX sybox p1box, sybox.Y + sybox.Height)
            container.Height <- container.Height + p1box.Height
        
            argbox.Location <- new PointF(sybox.Width, sybox.Y + (centerRelY sybox argbox))
            container.Width <- container.Width + argbox.Width
        
            node.MiddleY <- sybox.Y + sybox.Height / 2.f
        
            node.Box <- sybox
            p1.Container <- p1box
            p2.Container <- p2box
            arg.Container <- argbox
        
            ()


        |Sum(e1, e2) | Sub(e1, e2) | Equals(e1, e2) ->
            let symbol, name = match node.Expr with
                                |Sum(e1, e2) -> "+", "sum"
                                |Sub(e1, e2) -> "-", "sub"
                                |Equals(e1, e2) -> "=", "equals"
                                | _ -> "", ""

            let mutable sybox = measureString(g, symbol, font(level, false))
            container <- sybox
            eval1(g, e1, level)
            eval1(g, e2, level)
            node.ExprString <- name + "(" + e1.ExprString + ", " + e2.ExprString + ")"
            let mutable e1box = e1.Container
            let mutable e2box = e2.Container
        
            sybox.X <- e1box.Width
            e2box.X <- sybox.X + sybox.Width
            container.Width <- sybox.Width + e1box.Width + e2box.Width
        
            let localmiddleY = System.Math.Max(e1.MiddleY, e2.MiddleY)
            
            e1box.Y <- localmiddleY - e1.MiddleY
            e2box.Y <- localmiddleY - e2.MiddleY
            sybox.Y <- localmiddleY - sybox.Height / 2.f
        
            container.Height <- System.Math.Max(e1box.Y + e1box.Height, e2box.Y + e2box.Height)
        
            node.MiddleY <- sybox.Y + sybox.Height / 2.f
        
        
            //debug("Sum: sybox.width: " + sybox.Width.ToString() + "; e1box.width: " + e1box.Width.ToString() + "; e2box.width: " + e2box.Width.ToString() + " e2box.location: " + e2box.Location.ToString())
        
            node.Box <- sybox
        
            e1.Container <- e1box
            e2.Container <- e2box

            ()
        |Ratio(e1, e2) ->
            eval1(g, e1, level)
            eval1(g, e2, level)
            exprLib.debug "RATIO"
            node.ExprString <- "ratio(" + e1.ExprString + ", " + e2.ExprString + ")"
            let mutable e1box = e1.Container
            let mutable e2box = e2.Container
        
            container.Height <- e1box.Height + e2box.Height + 5.f
            container.Width <- System.Math.Max(e1box.Width, e2box.Width)
        
            e2box.Y <- e1box.Height + 5.f
            e2box.X <- container.Width / 2.f - e2box.Width / 2.f
        
            e1box.Y <- 0.f
            e1box.X <- container.Width / 2.f - e1box.Width / 2.f
        
            node.MiddleY <- e1box.Height + 3.f
        
            e1.Container <- e1box
            e2.Container <- e2box
        
            ()
        
        |Exponent(e1, e2) ->
            eval1(g, e1, level)
            eval1(g, e2, level+1)
        
            node.ExprString <- "exponent(" + e1.ExprString + ", " + e2.ExprString + ")"

            let mutable e1box = e1.Container
            let mutable e2box = e2.Container
        
            e1box.Y <- e2box.Height / 2.f
        
            (* e2box.X <- match e1.Expr with
                        |Subscript(_, _) -> e1.Box.X + e1.Box.Width
                        | _ -> e1box.X + e1box.Width 
            *)
            e2box.X <- e1box.Width
            node.MiddleY <- e1box.Y + e1box.Height / 2.f
            container.Width <- e1box.Width + e2box.Width
            container.Height <- e1box.Height + e1box.Y
            e1.Container <- e1box
            e2.Container <- e2box

        |Round(e) ->
            eval1(g, e, level)
            node.ExprString <- "round(" + e.ExprString + ")"

            let mutable ebox = e.Container

            let height = System.Math.Max(1.f, ebox.Height)

            let roundbox = new RectangleF(0.f, 0.f, height*25.f/105.f, height)//measureString(g, "(", new Font(stdfont,  height))

            //let gopen = openRound ebox
        

            ebox.X <- roundbox.Width


            ebox.Y <- centerRelY roundbox ebox
        
            ebox.Height <- height // it's used for font size in paintTree => should be greater than 0

            container.Width <- roundbox.Width*2.f + ebox.Width
            container.Height <- roundbox.Height + 1.f

            node.MiddleY <- ebox.Y + e.MiddleY
            node.Box <- roundbox

            e.Container <- ebox

       
            ()

        |Literal(str) ->
            let litbox = measureString(g, str, font(level, false))
            node.MiddleY <- litbox.Height / 2.f
            node.Box <- litbox
            container <- litbox
            node.ExprString <- str
        | _ ->  ()
        node.Container <- container
    


    
    let cdr (node:Expr, parentOffset:PointF) : PointF = new PointF(node.Container.X + parentOffset.X, node.Container.Y + parentOffset.Y)     


       //      d="m 303.94573,221.05469 c  -1.61616,-0.45742 -3.17015,-1.28287 -"
    //VIOLAZIONE SCHEMA MVC ==> pi-correlation in Paint!

    let rec paintTree(g:Graphics, offset:PointF, node:Expr, level, _minfo:mouseInfo) = 
        let mutable minfo = _minfo
        g.SmoothingMode <- System.Drawing.Drawing2D.SmoothingMode.AntiAlias 
        node.Offset <- offset
        let drawRectangle = new RectangleF(offset.X + node.Box.X, offset.Y + node.Box.Y, node.Box.Width, node.Box.Height)
        //let drawContainer = new RectangleF(offset.X, offset.Y, node.Container.Width, node.Container.Height)
        match node.Expr with 
        |BigSum(p1, p2, arg) ->
            let gp = exprLib.SumSymbol(drawRectangle)
            g.DrawPath(Pens.Black, gp)
        
            g.FillPath(Brushes.Black, gp)
            //g.DrawString(symbol, font(level, true), Brushes.Black, drawRectangle)
            paintTree(g, cdr(p1, offset), p1, level+1, minfo)
            paintTree(g, cdr(p2, offset), p2, level+1, minfo)
            paintTree(g, cdr(arg, offset), arg, level, minfo)
        |Sum(e1, e2) | Sub(e1, e2) | Equals(e1, e2) ->
            let symbol = match node.Expr with
                                |Sum(e1, e2) -> "+"
                                |Sub(e1, e2) -> "-"
                                |Equals(e1, e2) -> "="
                                | _ -> ""
            g.DrawString(symbol, font(level, false), Brushes.Black, drawRectangle)
            paintTree(g, cdr(e1, offset), e1, level, minfo)
            paintTree(g, cdr(e2, offset), e2, level, minfo)
        |Ratio(e1, e2) ->
            paintTree(g, cdr(e1, offset), e1, level, minfo)
            paintTree(g, cdr(e2, offset), e2, level, minfo)
            let lineY = int(offset.Y + e1.Container.Y + e1.Container.Height + 3.f)
            let startx = offset.X + 1.f
            exprLib.debug "PAINTING RATIO"
            g.DrawLine(Pens.Black, new Point(int startx, lineY), new Point(int (startx + node.Container.Width), lineY))
        |Exponent(e1, e2) ->
            paintTree(g, cdr(e1, offset), e1, level, minfo)
            paintTree(g, cdr(e2, offset), e2, level+1, minfo)
        |Round(e) ->
            let drawRectangle2 = new RectangleF(drawRectangle.X + drawRectangle.Width*2.f + e.Container.Width, drawRectangle.Y, drawRectangle.Width, drawRectangle.Height)
            let f = new Font(stdfont, e.Container.Height)
            //g.DrawString("(", f, Brushes.Black, drawRectangle)
            let gopen = exprLib.round(drawRectangle, 1.f)
            g.DrawPath(Pens.Black, gopen)
            g.FillPath(Brushes.Black, gopen)
            paintTree(g, cdr(e, offset), e, level, minfo)
            let gclose = exprLib.round(drawRectangle2, -1.f)
        
            g.DrawPath(Pens.Black, gclose)
            g.FillPath(Brushes.Black, gclose)
            //g.DrawString(")", f, Brushes.Black, drawRectangle2)
            //g.DrawRectangle(Pens.Red, Rectangle.Round(node.Box))
            ()
        
        |Literal(str) ->
            g.DrawString(str, font(level, false), Brushes.Black, drawRectangle)

        | _ -> ()
    
    
        if node.MouseOver then g.DrawRectangle(Pens.Black, Rectangle.Round(node.DrawContainer))
        if node.Clicked then 
            g.DrawRectangle(Pens.BlueViolet, Rectangle.Round(node.DrawContainer))
            g.FillRectangle(new SolidBrush(Color.FromArgb(60, Color.Blue)), Rectangle.Round(node.DrawContainer))
        ()




    let rec _mouseHandler (minfo:mouseInfo) (node:Expr) : Expr =

        let pos = minfo.position
    
        match node.Expr with
            |BigSum(p1, p2, p3) -> 
                //debug("bigsum")
                match true with
                    | _ when p1.DrawContainer.Contains(pos) -> _mouseHandler minfo p1
                    | _ when p2.DrawContainer.Contains(pos) -> _mouseHandler minfo p2
                    | _ when p3.DrawContainer.Contains(pos) -> _mouseHandler minfo p3
                    | _ -> node
            |Sum(p1, p2) | Sub(p1, p2)->
                match true with
                    | _ when p1.DrawContainer.Contains(pos) -> _mouseHandler minfo p1
                    | _ when p2.DrawContainer.Contains(pos) -> _mouseHandler minfo p2
                    | _ -> node
            |Ratio(p1, p2) ->
                match true with
                    | _ when p1.DrawContainer.Contains(pos) -> _mouseHandler minfo p1
                    | _ when p2.DrawContainer.Contains(pos) -> _mouseHandler minfo p2
                    | _ -> node
            |Exponent(p1, p2) ->
                match true with
                    | _ when p1.DrawContainer.Contains(pos) -> _mouseHandler minfo p1
                    | _ when p2.DrawContainer.Contains(pos) -> _mouseHandler minfo p2
                    | _ -> node
            |Round(p1) ->
                match true with
                    | _ when p1.DrawContainer.Contains(pos) -> _mouseHandler minfo p1
                    | _ -> node
            |Literal(str) -> node
            | _ -> Expr(Empty)

    let mouseHandler (minfo:mouseInfo) (node:Expr) : Expr =
        let mutable result = Expr(Empty)
        if node.DrawContainer.Contains(minfo.position) then
            result <- _mouseHandler minfo node
        result


    let rec visitTree (e:Expr) = 
        match e.Expr with
            |BigSum(p1, p2, arg) -> exprLib.debug("BigSum: " + e.Box.ToString()); visitTree(p1); visitTree(p2); visitTree(arg)

            |Literal(p1) -> exprLib.debug("Literal: " + p1 + "; " + e.Box.ToString()); 
            | _ -> ()
        ()


    //sum(ratio(bigsum(i=0, 10, x), 2), ratio(exp(4, 2), sum(6, 7)))


    let mutable e = Expr(Round(Expr(Sum(
                                        Expr(Ratio(
                                                Expr(BigSum(
                                                        Expr(Literal("i=0")), 
                                                        Expr(Literal("10")), 
                                                        Expr(Literal("x")))), 
                                                Expr(Literal("2")))), 
                                        Expr(Ratio(
                                                Expr(Exponent(Expr(Literal("4")), Expr(Literal("2")))), 
                                                //Expr(Literal("3")),
                                                //Expr(Literal("4"))))))
                                                //Expr(Exponent(Expr(Literal("4")), Expr(Literal("2"))))))))
                                                Expr(Round(Expr( Sum(Expr(Literal("6")), Expr(Literal("7"))))))
                                                )))) ))

    let toPointF (p:Point) = PointF(float32 p.X, float32 p.Y)








    let rec parseE(tokens:string[], next:int byref ) : Expr = 
        //let tokens = str.Trim().ToLower().Split([| '('; ','; ')' |])
        //let mutable next = _next
    
        let mutable currentexpr = Expr(Empty)//parseE(tokens, &next)
        let mutable stop = false
        while next < tokens.Length && not stop do
            let mval = tokens.[next].Trim()
            currentexpr <- match mval with
                            |"sum" | "sub" -> 
                                exprLib.debug("parseE: sum/sub")
                                next <- next + 2
                                let child1 = parseE(tokens, &next)
                                next <- next + 1
                                let sumExpr = match mval with
                                                |"sum" -> Expr(Sum(child1, parseE(tokens, &next)))
                                                |"sub" -> Expr(Sub(child1, parseE(tokens, &next)))
                                                |"" -> Expr(Empty)
                                exprLib.debug("parseE: sum: skipping )")
                                next <- next + 1
                                sumExpr
                                //parseSum(tokens, next+1)
                            |"ratio" ->
                                next <- next + 2
                                let child1 = parseE(tokens, &next)
                                next <- next + 1
                                let child2 = parseE(tokens, &next)
                                let ratioExpr = Expr(Ratio(child1, child2))
                                next <- next + 1
                                ratioExpr
                            | "bigsum" ->
                                next <- next + 2
                                let child1 = parseE(tokens, &next)
                                next <- next + 1
                                let child2 = parseE(tokens, &next)
                                next <- next + 1
                                let bigsumExpr = Expr(BigSum(child1, child2, parseE(tokens, &next)))
                                next <- next + 1
                                bigsumExpr
                            | "+" | "-" ->
                                exprLib.debug("sum contract")
                                next <- next + 1
                                let secondchild = parseE(tokens, &next)
                                exprLib.debug("sum contract returning")
                                match mval with
                                    |"+" -> Expr(Sum(currentexpr, secondchild))
                                    |"-" -> Expr(Sub(currentexpr, secondchild))
                                    | _ -> Expr(Empty)  
                            | "/" ->
                                exprLib.debug("ratio contract")
                                next <- next + 1
                                let secondchild = parseE(tokens, &next)
                                Expr(Ratio(currentexpr, secondchild)) 
                            | "=" ->
                                exprLib.debug("equals")
                                next <- next + 1
                                let secondchild = parseE(tokens, &next)
                                Expr(Equals(currentexpr, secondchild))
                            | "(" ->
                                next <- next + 1
                                exprLib.debug("opened (")
                                let roundedExpr = Expr(Round(parseE(tokens, &next)))
                                match tokens.[next] with
                                    | ")" -> exprLib.debug("closed )"); next <- next + 1; roundedExpr
                                    | _ -> Expr(Literal("no match )")) 
                            | "," -> exprLib.debug("found: ,; stopping"); stop <- true; currentexpr 
                            | ")" -> stop <- true; currentexpr
                            | _ -> 
                                exprLib.debug("parseE: noMatch: " + tokens.[next].ToString())
                                let cur = tokens.[next]
                                next <- next + 1
                            
                                match cur with
                                    | "" -> currentexpr
                                    | _ -> exprLib.debug("parseE: noMatch: return literal: " + cur); Expr(Literal(cur))
                                //currentexpr
                            

            (*match tokens.[next] with
                | "+" -> next <- next + 1; currentexpr <- Expr(Sum(currentexpr, parseE(tokens, &next)))
                | _ -> ()
            *)
            ()

        currentexpr


        //Array.iter(fun el -> debug("current token: " + el.ToString()))tokens
    
            
    let tokenizer(str:string, chars:char[]) : string[] = 
        let mutable result = [| |]
        let mutable currentstring = ""
        for i=0 to str.Length - 1 do
            let cchar = str.Chars(i)
            if Array.exists(fun el -> el.Equals(cchar))chars then  
                result <- Array.append result [| currentstring; cchar.ToString() |]
                currentstring <- ""
            else currentstring <- currentstring + cchar.ToString()
        if currentstring <> "" then result <- Array.append result [| currentstring |]
        result



    let parse2(_str:string) : Expr =
        let mutable next = 0
        //let symbols = [| "+"; "/"; "-"; "*" |]
        //let mutable str = _str.Replace("+", ",+,").Replace("-", ",-,").Replace("*", ",*,").Replace("/", ",/,").Replace("(", ",(,").Replace(")", ",),")
        let str = _str.Replace(" ", "").ToLower()
        //debug("String token: " + str)
        //Array.iter(fun (e:string) -> str <- str.Replace(e, "," + e + ","))symbols
        let tokens = tokenizer(str, [| '+'; '-'; '*'; '/'; '('; ')'; ','; '=' |])
    
        let mutable currentexpr = parseE(tokens, &next)

        currentexpr
        //Expr(Empty)

    e <- Expr(Empty)

    //e <- parse2("sum(2, ratio(bigsum(2, 3, 4), 3))")
    type myTextBox() as x =
        inherit RichTextBox()

        let mutable highlighted = false
    
        let mutable firstpos = -1
        let mutable lastpos = -1
        let mutable ignore = false

        let deselect (currentpos:int) = 
            highlighted <- false
            exprLib.debug("deselecting")
            x.SelectAll()
            //x.Select(firstpos, 1)
            x.SelectionColor <- Color.Black
            //x.Select(lastpos, 1)
            //x.SelectionColor <- Color.Black

            x.Select(currentpos + 1, 0)
            firstpos <- -1
            lastpos <- -1

        let selectPos current firstpos lastpos = 
            highlighted <- true
            //firstpos <- pos
            //lastpos <- i-1
            //debug("info: i: " + i.ToString())
            exprLib.debug("firstpos: " + firstpos.ToString() + "; lastpos: " + lastpos.ToString())
            ignore <- true
            x.SelectionBackColor <- Color.White
            x.Select(firstpos, 1)
            x.SelectionColor <- Color.Red
            x.Select(lastpos, 1)
            x.SelectionColor <- Color.Red

            x.Select(current, 0)
            ignore <- false

        override x.OnSelectionChanged arg =
            //base.OnSelectionChanged(arg)
        
            let pos = x.SelectionStart - 1 
            let mutable str = x.Text
            if highlighted && not ignore then deselect(pos)

            if (pos>=0 && not ignore) then
                match x.Text.Chars(pos) with
                    |'('->  
                    
                        let mutable opened = 1
                        let mutable i = pos + 1

                        while (i < str.Length && opened>0) do
                            match str.Chars(i) with
                                | '(' -> opened <- opened + 1
                                | ')' -> opened <- opened - 1
                                | _ -> ()
                            //debug("opened: " + opened.ToString())
                            i <- i+1
                        if (opened = 0 && i>0) then
                            selectPos (pos+1) (pos) (i-1)
                    | ')' ->
                        let mutable closed = 1
                        let mutable i = pos - 1

                        while (i >= 0 && closed>0) do
                            match str.Chars(i) with
                                | '(' -> closed <- closed - 1
                                | ')' -> closed <- closed + 1
                                | _ -> ()
                            i <- i-1
                        if (closed = 0) then
                            selectPos (pos+1) (i+1) (pos)
                    | _ -> ()

           
                x.Invalidate()
            ()



    type myForm() as x =
        inherit Form()
    
        let mutable minfo = new mouseInfo(false, Over, new PointF(0.f, 0.f))
        let mutable treeValid = false
        let mutable lastOver = Expr(Empty)
        let mutable lastClicked = Expr(Empty)

        let mutable bitmap = new Bitmap(1, 1)

        let txtinput = new myTextBox()

        let setText str = 
            txtinput.Text <- str
            txtinput.SelectionStart <- txtinput.Text.Length

        do
            x.Controls.Add(txtinput)
            txtinput.Dock <- DockStyle.Bottom
            txtinput.Font <- new Font(stdfont, 15.f)
        
            txtinput.TextChanged.Add(fun arg ->
                                        match lastClicked.Expr with
                                            |Empty when not (e.Expr = Empty) -> exprLib.debug("text ignore")
                                            |_ -> 
                                                try
                                                    exprLib.debug("text ok")
                                                    let newe = parse2(txtinput.Text)
                                                    match e.Expr with
                                                        | Empty -> e.Expr <- newe.Expr
                                                                   e.Clicked <- true
                                                                   lastClicked <- e
                                                        | _ -> lastClicked.Expr <- newe.Expr
                                                    treeValid <- false
                                                    x.Invalidate()
                                                with
                                                    | err -> exprLib.debug("Errore: " + err.ToString()) 
                                                ()
                                        ())

            ()
    
        override x.OnPaint arg = 
            if (not treeValid) then 
                eval1(arg.Graphics, e, 0)
                treeValid <- true
        
            bitmap.Dispose()
            
            bitmap <- new Bitmap(x.Width, x.Height - txtinput.Height)
        
            let g1 = Graphics.FromImage(bitmap)
        
            g1.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height)
        
            //debug("e container widht: " + e.Container.Width.ToString())
            let off = new PointF(float32 bitmap.Width / 2.f - e.Container.Width / 2.f, float32 bitmap.Height / 2.f - e.Container.Height / 2.f)
        
            paintTree(g1, off, e, 0, minfo)
   
            arg.Graphics.DrawImage(bitmap, 0, 0)
        
            System.GC.Collect()
            ()
        
        override x.OnMouseMove arg =
            minfo.handle <- true
            minfo.event <- Over 
            minfo.position <- toPointF(arg.Location)
            lastOver.MouseOver <- false
            lastOver <- mouseHandler minfo e
            lastOver.MouseOver <- true
            match lastClicked.Expr with
                |Empty ->
                    setText lastOver.ExprString 
                    //txtinput.Text <- lastOver.ExprString
                    //txtinput.Invalidate()
                | _ -> ()

            x.Invalidate()
            ()
        
        
        override x.OnMouseDown arg = 
            minfo.handle <- true
            minfo.event <- Click
            minfo.position <- toPointF(arg.Location) 
            lastClicked.Clicked <- false
            lastClicked <- mouseHandler minfo e
            lastClicked.Clicked <- true
            setText lastClicked.ExprString
            exprLib.debug("MouseClick")
            treeValid <- false
            x.Invalidate()
            ()
        
        override x.OnPaintBackground arg =
    
            ()
        
        
    let f = new myForm()
    f.TopMost <- true
    f.Size <- new Size(720, 420)
    f.Show()


